package org.tbjd.client.customWidgets.taskList;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.tbjd.client.constants.NotificationsConstants;
import org.tbjd.client.dndControllers.taskList.FlexTableRowDragController;
import org.tbjd.client.dndControllers.taskList.FlexTableRowDropController;
import org.tbjd.client.exceptions.NoUpperDecisionmakerFoundException;
import org.tbjd.client.exceptions.NoUpperMediatorFoundException;
import org.tbjd.client.images.taskList.TaskListImageBundle;
import org.tbjd.client.interfaces.Decisionmaker;
import org.tbjd.client.interfaces.HasDragController;
import org.tbjd.client.interfaces.HasMediator;
import org.tbjd.client.mediators.AbstractMediator;

import com.allen_sauer.gwt.dnd.client.DragController;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.Widget;

public class TreeGrid extends Composite implements HasMediator, Decisionmaker, HasDragController {

	AbsolutePanel container = new AbsolutePanel();
	FlexTable grid = new FlexTable();
	TreeGridItem columnHeadersItem = new TreeGridItem();

	List<TreeGridItem> lstTreeGridItems = null;
	ArrayList<Widget> columnHeaders = null;

	FlexTableRowDragController flexTableDragController = null;
	FlexTableRowDropController dropController = null;

	public TreeGrid() {
		container.add(grid);
		initWidget(container);
		grid.setStyleName("treeGrid");
	}

	public TreeGrid(ArrayList<Widget> columnHeader) {

		this.columnHeaders = columnHeader;

		// Add the column for the collapse button
		this.columnHeaders.add(0, new Label(" "));

		columnHeadersItem.setValues(this.columnHeaders);

		for (int i = 0; i < columnHeadersItem.getValues().size(); i++) {
			Widget widget = columnHeadersItem.getValues().get(i);

			grid.setWidget(0, i, widget);
			grid.getCellFormatter().setStyleName(0, i, "entete");
		}

		container.add(grid);
		initWidget(container);
		grid.setStyleName("treeGrid");

		flexTableDragController = new FlexTableRowDragController(container);
		dropController = new FlexTableRowDropController(grid);
		flexTableDragController.registerDropController(dropController);
	}

	public void setColumnsWidth(String[] colWidth) throws Exception {

		String[] columnsWidth = new String[colWidth.length + 1];
		columnsWidth[0] = "20px";

		for (int i = 1; i < columnsWidth.length; i++) {
			columnsWidth[i] = colWidth[i - 1];
		}

		if (columnsWidth == null || columnsWidth.length != grid.getCellCount(0)) {
			throw new Exception("The columns number must match with the gridTable columns number");
		}

		grid.getCellFormatter().setWidth(0, 0, "20px");

		for (int i = 0; i < columnsWidth.length; i++) {
			grid.getCellFormatter().setWidth(0, i, columnsWidth[i]);
		}
	}

	private void drawRows(final TreeGridItem treeGridItem) {
		writeRow(treeGridItem, grid.getRowCount(), treeGridItem.hasChildren());

		if (treeGridItem.hasChildren()) {
			for (TreeGridItem treeGridItemChild : treeGridItem.getChildren()) {
				writeRow(treeGridItemChild, grid.getRowCount(), treeGridItemChild.hasChildren());
			}
		}
	}

	private void writeRow(final TreeGridItem treeGridItem, final int rowToWrite, boolean isParent) {

		ArrayList<Widget> lstValuesWidget = treeGridItem.getValues();

		for (int i = 0; i < lstValuesWidget.size(); i++) {
			Widget eachWidget = lstValuesWidget.get(i);
			grid.setWidget(rowToWrite, i + 1, eachWidget);
			if (isParent) {
				TaskListImageBundle taskImageBundle = (TaskListImageBundle) GWT.create(TaskListImageBundle.class);
				ToggleButton pbCollapse = new ToggleButton(taskImageBundle.plus13x13().createImage(), taskImageBundle.minus13x13().createImage());
				pbCollapse.setStyleName("outlineNone");
				grid.setWidget(rowToWrite, 0, pbCollapse);

				pbCollapse.addClickListener(getCollapseListener(treeGridItem));
			}
		}

		flexTableDragController.makeDraggable(((TreeGridItemHandel) lstValuesWidget.get(0)).getHandel());

	}

	private ClickListener getCollapseListener(final TreeGridItem treeGridItem) {
		return new ClickListener() {
			public void onClick(Widget sender) {

				ToggleButton toggleButton = (ToggleButton) sender;

				int row = getWidgetRow(toggleButton, 0);

				for (int i = row + 1; i <= row + treeGridItem.getChildren().size(); i++) {
					grid.getRowFormatter().setVisible(i, !grid.getRowFormatter().isVisible(i));
				}
			}
		};
	}

	public void clear() {

		grid.clear();

		columnHeadersItem.setValues(this.columnHeaders);

		for (int i = 0; i < columnHeadersItem.getValues().size(); i++) {
			Widget widget = columnHeadersItem.getValues().get(i);

			grid.setWidget(0, i, widget);
			grid.getCellFormatter().setStyleName(0, i, "entete");

		}
	}

	public void display(List<?> items) {
		for (TreeGridItem eachItem : lstTreeGridItems) {
			drawRows(eachItem);
		}

		this.toString();
		System.out.println("*****************************");
	}

	public void setMediator(AbstractMediator mediator) {
	}

	public void decide(Object action) {
		System.out.println(this.getClass().getName() + " decide()");
		if (((Map<?, ?>) action).containsKey(NotificationsConstants.TREEGRIDITEM_MOVED)) {
			System.out.println("TreeGrid.decide() TREEGRIDITEM_MOVED");
			updateItemsAndBeans(action);
		} else if (((Map<?, ?>) action).containsKey(NotificationsConstants.DRAGGING_TREEGRIDITEM_SOURCE)) {
			System.out.println(this.getClass().getName() + " decide() -- DRAGGING_TREEGRIDITEM_SOURCE");

			// TODO to refactor into an external class
			int originalWidget = ((Map<?, Integer>) action).get(NotificationsConstants.DRAGGING_TREEGRIDITEM_SOURCE);
			int targetWidget = ((Map<?, Integer>) action).get(NotificationsConstants.DRAGGING_TREEGRIDITEM_TARGET);

			TreeGridItem gridItemDragged = this.getTreeGridItem(originalWidget);
			TreeGridItem gridItemTarget = this.getTreeGridItem(targetWidget);

			System.out.println(gridItemTarget.getBean().getName());

			// check if the gridItemDragged is a phase, task or subtask
			if (gridItemDragged.hasChildren()) {

				// check if the gridItemTarget is a phase, task or subtask
				if (gridItemTarget.hasChildren()) {
					System.out.println("(Preview drag) phase dragged into a phase : it's not allowed");
					// return false;
				} else if (gridItemTarget.getParent() == null) {
					System.out.println("(Preview drag) phase dragged into a task : move phase and children (done)");
					// return true;
				} else {
					int positionInChildren = gridItemTarget.getParent().getChildren().indexOf(gridItemTarget);
					if (gridItemTarget.getParent().getChildren().size() - 1 == positionInChildren) {
						System.out.println("(Preview drag) phase dragged into the last subtask : do something");
						// return true;
					} else {
						System.out.println("(Preview drag) phase dragged into a subtask : not allowed");
						// return false;
					}
				}

			} else if (gridItemDragged.getParent() == null) {

				// check if the gridItemTarget is a phase, task or subtask
				if (gridItemTarget.hasChildren()) {
					System.out.println("(Preview drag) task dragged into a phase : (done)");
					// return true;
				} else if (gridItemTarget.getParent() == null) {
					System.out.println("(Preview drag) task dragged into a task : (done)");
					// return true;
				} else {
					System.out.println("(Preview drag) task dragged into a subtask : (done)");
					// return true;
				}
			} else {
				detach(gridItemDragged);

				// check if the gridItemTarget is a phase, task or subtask
				if (gridItemTarget.hasChildren()) {
					System.out.println("(Preview drag) subtask dragged into a phase : (done)");
					// return true;
				} else if (gridItemTarget.getParent() == null) {
					System.out.println("(Preview drag) subtask dragged into a task : (done)");
					// return true;
				} else {
					System.out.println("(Preview drag) subtask dragged into a subtask : (done)");
					// return true;

				}
			}

		}
	}

	/**
	 * Update TreeGridItem(s) information as well as related beans
	 * 
	 * @param action
	 */
	public void updateItemsAndBeans(Object action) {

		int originPosition = (Integer) ((Map<?, ?>) action).get(NotificationsConstants.TREEGRIDITEM_MOVED_ORIGIN_POSITION);
		int targetPosition = (Integer) ((Map<?, ?>) action).get(NotificationsConstants.TREEGRIDITEM_MOVED_TARGET_POSITION);

		TreeGridItem gridItemDragged = getTreeGridItem(originPosition);
		TreeGridItem gridItemTarget = getTreeGridItem(targetPosition);

		System.out.println("TreeGrid.updateItemsAndBeans() origin bean=" + gridItemDragged.getBean().getName());
		System.out.println("TreeGrid.updateItemsAndBeans() target bean=" + gridItemTarget.getBean().getName());

		// check if the gridItemDragged is a phase, task or subtask
		if (gridItemDragged.hasChildren()) {

			// check if the gridItemTarget is a phase, task or subtask
			if (gridItemTarget.hasChildren()) {
				System.out.println("phase dragged into a phase : it's not allowed");
			} else if (gridItemTarget.getParent() == null) {
				System.out.println("phase dragged into a task : move phase and children (done)");
				System.out.println("origin position " + originPosition);
				System.out.println("target position " + targetPosition);

				for (int i = 0; i < gridItemDragged.getChildren().size(); i++) {
					FlexTableUtil.moveRow(grid, grid, (originPosition + 1), (targetPosition + 2));
				}

				lstTreeGridItems.remove(gridItemDragged);
				lstTreeGridItems.add(lstTreeGridItems.indexOf(gridItemTarget) + 1, gridItemDragged);
			} else {
				int positionInChildren = gridItemTarget.getParent().getChildren().indexOf(gridItemTarget);
				if (gridItemTarget.getParent().getChildren().size() - 1 == positionInChildren) {
					System.out.println("phase dragged into the last subtask : do something");
					for (int i = 0; i < gridItemDragged.getChildren().size(); i++) {
						FlexTableUtil.moveRow(grid, grid, (originPosition + 1), (targetPosition + 2));
					}

					lstTreeGridItems.remove(gridItemDragged);
					lstTreeGridItems.add(lstTreeGridItems.indexOf(gridItemTarget.getParent()) + 1, gridItemDragged);

				} else {
					System.out.println("phase dragged into a subtask : not allowed");
				}
			}

		} else if (gridItemDragged.getParent() == null) {

			// check if the gridItemTarget is a phase, task or subtask
			if (gridItemTarget.hasChildren()) {
				System.out.println("task dragged into a phase : (done)");
				lstTreeGridItems.remove(gridItemDragged);
				gridItemTarget.getChildren().add(0, gridItemDragged);
				gridItemDragged.setParent(gridItemTarget);
				gridItemDragged.setStyleName("subtask");
			} else if (gridItemTarget.getParent() == null) {
				System.out.println("task dragged into a task : (done)");
				lstTreeGridItems.remove(gridItemDragged);
				lstTreeGridItems.set(lstTreeGridItems.indexOf(gridItemTarget) + 1, gridItemDragged);
			} else {
				System.out.println("task dragged into a subtask : (done)");
				int positionInChildrenList = gridItemTarget.getParent().getChildren().indexOf(gridItemTarget);
				gridItemTarget.getParent().getChildren().add(positionInChildrenList + 1, gridItemDragged);
				gridItemDragged.setParent(gridItemTarget.getParent());
				gridItemDragged.setStyleName("subtask");
			}
		} else {
			detach(gridItemDragged);

			// check if the gridItemTarget is a phase, task or subtask
			if (gridItemTarget.hasChildren()) {
				System.out.println("subtask dragged into a phase : (done)");
				gridItemDragged.getParent().getChildren().remove(gridItemDragged);
				gridItemTarget.getChildren().add(0, gridItemDragged);
				gridItemDragged.setParent(gridItemTarget);
			} else if (gridItemTarget.getParent() == null) {
				System.out.println("subtask dragged into a task : (done)");
				gridItemDragged.setParent(null);
				lstTreeGridItems.add(lstTreeGridItems.indexOf(gridItemTarget) + 1, gridItemDragged);
				gridItemDragged.setStyleName("task");
			} else {
				System.out.println("subtask dragged into a subtask : (done)");
				int positionInChildrenList = gridItemTarget.getParent().getChildren().indexOf(gridItemTarget);
				gridItemDragged.getParent().getChildren().remove(gridItemDragged);
				gridItemTarget.getParent().getChildren().add(positionInChildrenList + 1, gridItemDragged);

				gridItemDragged.setParent(gridItemTarget.getParent());
				System.out.println("subtask dragged " + gridItemDragged.getBean().getName());

			}
		}
		compute(originPosition, targetPosition);
		// this.toString();
	}

	/**
	 * detach a gridItem from its previous parent
	 * 
	 * @param gridItem
	 */
	private void detach(TreeGridItem gridItem) {
		gridItem.getParent().getChildren().remove(gridItem);
	}

	/**
	 * Return the TreeGridItem corresponding to the given rowPosition
	 * 
	 * @param originPosition
	 * @return the TreeGridItem or null if not found
	 */
	private TreeGridItem getTreeGridItem(int originPosition) {
		System.out.println(this.getClass() + ".getTreeGridItem(" + originPosition + ")");
		for (TreeGridItem eachItem : lstTreeGridItems) {
			if (eachItem.hasChildren()) {
				for (TreeGridItem eachChildItem : eachItem.getChildren()) {
					if (eachChildItem.getPosition() == originPosition) {
						return eachChildItem;
					}
				}
			}
			if (eachItem.getPosition() == originPosition) {
				return eachItem;
			}

		}
		return null;
	}

	/**
	 * Return the row corresponding to the given Widget
	 * 
	 * @param widgetToFind
	 * @param columnToSearch
	 *            the grid column to search in
	 * @return the row or -1 if not found
	 */
	public int getWidgetRow(Widget widgetToFind, int columnToSearch) {

		for (int i = 0; i < grid.getRowCount(); i++) {

			if (columnToSearch != -1) {
				Widget widget = grid.getWidget(i, columnToSearch);
				if (widget != null && grid.getWidget(i, columnToSearch).equals(widgetToFind)) {
					return i;
				}
			} else {
			}
		}

		return -1;
	}

	public void delegate(Object action) throws NoUpperDecisionmakerFoundException, NoUpperMediatorFoundException {
	}

	public void handle(Object action) {
	}

	public void setDragController(DragController dragController) {
	}

	public void setTreeGridItems(List<TreeGridItem> lstTreeGridItem) {
		this.lstTreeGridItems = lstTreeGridItem;
		display(lstTreeGridItem);
	}

	public String toString() {

		int i = 0;

		for (TreeGridItem eachTreeGridItem : lstTreeGridItems) {
			System.out.println(i + ")\t\t " + eachTreeGridItem.getBean().getName() + "\t - pos:" + eachTreeGridItem.getBean().getLevel());
			i++;
			if (eachTreeGridItem.hasChildren()) {
				for (TreeGridItem eachChild : eachTreeGridItem.getChildren()) {
					System.out.println(i + ")\t " + eachChild.getBean().getName() + "\t - pos:" + eachChild.getBean().getLevel());
					i++;
				}
			}
		}

		return super.toString();
	}

	/**
	 * This method compute the position of the TreeGridItems
	 * 
	 * @param startPosition
	 * @param endPosition
	 */
	private void compute(int startPosition, int endPosition) {

		int cpt = 0;

		for (int i = 0; i < this.lstTreeGridItems.size(); i++) {
			TreeGridItem treeGridItem = this.lstTreeGridItems.get(i);
			treeGridItem.setPosition(cpt);
			treeGridItem.getBean().setLevel(cpt);
			cpt++;
			if (treeGridItem.hasChildren()) {
				for (int j = 0; j < treeGridItem.getChildren().size(); j++) {
					TreeGridItem treeGridChildItem = treeGridItem.getChildren().get(j);
					treeGridChildItem.setPosition(cpt);
					treeGridChildItem.getBean().setLevel(cpt);
					cpt++;
				}
			}
		}
	}

}
